<?php
namespace Common\Org\Wxapi;

use Common\Org\Wxapi\SDKRuntimeException;
use Common\Org\Wxapi\WxConf;

/**
 * 微信基础接口实现类
 * 		createOverallAccessToken用于生成全局的access_token
 */
class WxBaseInterfaceUtil {
	
	public $code;//code码，用以获取openid
	public $openid;//用户的openid
	public $appid; //微信公众号appid
	public $appsecret; //微信公众号密钥
	public $overall_access_token;//全局access_token
	public $oauth_access_token; //显示授权access_token
	public $curl_timeout;//curl超时时间
	public $ticket; // 二维码ticket
	
	function __construct() {
		//设置curl超时时间
		$this->curl_timeout = WxConf::CURL_TIMEOUT;
		//取出的appid和appsecret
// 		$url_path = dirname(dirname(__FILE__));
		
		$url_path = C('wesetDir');
		
		$xml_path = $url_path."/lib/config/app.xml";
		if(file_exists($xml_path)) {
			$app_xml = (array)simplexml_load_file($xml_path);
			$this->appid = $app_xml['appId'];
			$this->appsecret = $app_xml['appSecret'];
		} else {
			echo "not find appId and appSecret";
			exit();
		}
	}
	
	public	function trimString($value) {
			$ret = null;
			if (null != $value)
			{
				$ret = $value;
				if (strlen($ret) == 0)
				{
					$ret = null;
				}
			}
			return $ret;
		}
	
		/**
		 * 	作用：格式化参数，签名过程需要使用
		 */
		function formatBizQueryParaMap($paraMap, $urlencode) {
			$buff = "";
			ksort($paraMap);
			foreach ($paraMap as $k => $v) {
				if($urlencode)
				{
					$v = urlencode($v);
				}
				//$buff .= strtolower($k) . "=" . $v . "&";
				$buff .= $k . "=" . $v . "&";
			}
			$reqPar = '';
			if (strlen($buff) > 0)
			{
				$reqPar = substr($buff, 0, strlen($buff)-1);
			}
			return $reqPar;
		}
	
		/**
		 * 	作用：array转xml
		 */
		function arrayToXml($arr) {
			$xml = "<xml>";
			foreach ($arr as $key=>$val)
			{
				if (is_numeric($val))
				{
					$xml.="<".$key.">".$val."</".$key.">";
		
				}
				else
					$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
			}
			$xml.="</xml>";
			return $xml;
		}
	
		/**
		 * 	作用：将xml转为array
		 */
		public function xmlToArray($xml) {
			//将XML转为array
			$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
			return $array_data;
		}
	
		/**
		 * 	作用：以post方式提交xml到对应的接口url
		 */
		public function postXmlCurl($xml,$url,$second=30) {
			//初始化curl
			$ch = curl_init();
			//设置超时
			curl_setopt($ch, CURLOP_TIMEOUT, $second);
			//这里设置代理，如果有的话
			//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
			//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
			curl_setopt($ch,CURLOPT_URL, $url);
			curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
			curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
			//设置header
			curl_setopt($ch, CURLOPT_HEADER, FALSE);
			//要求结果为字符串且输出到屏幕上
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
			//post提交方式
			curl_setopt($ch, CURLOPT_POST, TRUE);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
			//运行curl
			$data = curl_exec($ch);
			curl_close($ch);
			//返回结果
			if($data) {
				curl_close($ch);
				return $data;
			}
			else {
				$error = curl_errno($ch);
				echo "curl出错，错误码:$error"."<br>";
				curl_close($ch);
				return false;
			}
		}
	
	
		/**
		* 	作用：打印数组
			*/
		public function printErr($wording='',$err='') {
			print_r('<pre>');
			echo $wording."</br>";
			var_dump($err);
			print_r('</pre>');
		}
	
		public function httpGet($url) {
			$curl = curl_init();
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($curl, CURLOPT_TIMEOUT, 500);
			curl_setopt($curl, CURLOPT_URL, $url);
	
			$res = curl_exec($curl);
			curl_close($curl);
	
			return $res;
		}
	
	/**
	 * 	作用：设置code
	 */
	function setCode($code_) {
		$this->code = $code_;
	}
	
	/**
	 * 	作用：设置openid
	 */
	function setOpenid($openid_) {
		$this->openid = $openid_;
	}
	
	/**
	 * 作用：设置全局access_token
	 */
	function setOverallAccessToken($overall_access_token_) {
		$this->overall_access_token = $overall_access_token_;
	}
	
	/**
	 * 作用：设置授权access_token
	 */
	function setOauthAccessToken($oauth_access_token_) {
		$this->oauth_access_token = $oauth_access_token_;
	}
	
	/**
	 * 	作用：设置ticket
	 */
	function setTicket($ticket_) {
		$this->ticket = $ticket_;
	}
	
	/**
	 * 授权跳转链接
	 */
	function createOauthUrlForCode($redirectUrl){
		$urlObj["appid"] = $this->appid;
		$urlObj["redirect_uri"] = "$redirectUrl";
		$urlObj["response_type"] = "code";
		$urlObj["scope"] = "snsapi_base";
		$urlObj["state"] = "STATE"."#wechat_redirect";
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
	}
	
	/**
	 * 	作用：生成可以获得全局access_token的url
	 */
	function createOverallAccessTokenUrl()
	{
		$urlObj["grant_type"] = "client_credential";
		$urlObj["appid"] = $this->appid;
		$urlObj["secret"] = $this->appsecret;
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::OVERALL_ACCESS_TOKEN_URL.$bizString;
	}
	
	/**
	 * 	作用：生成可以获得openid和授权access_token的url
	 */
	function createOauthUrlForOpenidAndAccessToken()
	{
		$urlObj["appid"] = $this->appid;
		$urlObj["secret"] = $this->appsecret;
		$urlObj["code"] = $this->code;
		$urlObj["grant_type"] = "authorization_code";
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::OAUTH_OPENID_URL.$bizString;
	}
	
	/**
	 * 作用：生成获取用户信息的url
	 */
	function createGainUserInfoUrl() {
		$urlObj["access_token"] = $this->overall_access_token;
		//$urlObj["openid"] = $nex_openid != '' ? $nex_openid : $this->openid;
		$urlObj["openid"] = $this->openid;
		$urlObj["lang"] = "zh_CN";
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::GAIN_USERINFO_BY_OVERALL_ACCESS_TOKEN_URL.$bizString;
	}
	
	/**
	 * 作用：生成显示授权获取用户信息的url
	 */
	function createOauthUserInfoUrl() {
		$urlObj["access_token"] = $this->oauth_access_token;
		$urlObj["openid"] = $this->openid;
		$urlObj["lang"] = "zh_CN";
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::SNSAPI_USERINFO_BY_OAUTH_ACCESS_TOKEN_URL.$bizString;
	}
	
	/**
	 * 作用：生成推广二维码的url
	 */
	function createQrcodeTicketUrl() {
		$urlObj["access_token"] = $this->overall_access_token;
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::GET_QRCODE_TICKET_URL.$bizString;
	}
	
	/**
	 * 作用：通过ticket换取二维码
	 */
	function createTicketGetQrcodeUrl() {
		$urlObj["ticket"] = urlencode($this->ticket);
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::PASS_THROUGH_TICKET_GET_QECODE_URL.$bizString;
	}
	
	/**
	 * 作用：获取客服基本信息
	 */
	function createGetKFListUrl() {
		$urlObj["access_token"] = $this->overall_access_token;
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::CUSTOM_SERVICE_GET_KF_LIST.$bizString;
	}
	
	/**
	 * 作用：创建获取js的ticket
	 */
	function createGetJsTicket() {
		$urlObj["type"] = "jsapi";
		$urlObj["access_token"] = $this->overall_access_token;
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::GET_JS_SDK_TICKET_URL.$bizString;
	}
	
	/**
	 * 作用获取关注者列表url
	 */
	function createGetSubscribeList($next_openid) {
		$urlObj["access_token"] = $this->overall_access_token;
		$urlObj["next_openid"] = $next_openid; //第一个拉取的OPENID，不填默认从头开始拉取
		$bizString = $this->formatBizQueryParaMap($urlObj, false);
		return WxConf::GET_SUBSCRIBE_USER_LIST.$bizString;
	}
	
	/**
	 * 作用：通过授权的code获取用户openid和授权的access_token
	 */
	function getUserOpenidAndOauthAccessTokenByCode() {
		
		$url = $this->createOauthUrlForOpenidAndAccessToken();
		//初始化curl
       	$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
		curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		//运行curl，结果以jason形式返回
        $res = curl_exec($ch);
		curl_close($ch);
		//取出openid
		$data = json_decode($res,true);
		//$this->openid = $data['openid'];
		//$this->oauth_access_token = $data['access_token'];
		return $data;
	}
	
	/**
	 * 作用：获取全局access_token
	 */
	function getOverallAccessToken() {
		$url = $this->createOverallAccessTokenUrl();
		
		//初始化curl
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		//运行curl，结果以jason形式返回
		$res = curl_exec($ch);
		curl_close($ch);
		//取出openid
		$data = json_decode($res,true);
// 		p($data);
		//$this->overall_access_token = $data['access_token'];
		return $data['access_token'];
	}
	
	/**
	 * 作用：通过全局access_token获取用户信息
	 */
	function getUserInfoByOverallAccessToken($openid) {
		//$this->getUserOpenidAndOauthAccessTokenByCode(); //通过code获取openid
		//获取全局access_token
		$this->openid = $openid;
		$this->getLocalAccessToken();
		$url = $this->createGainUserInfoUrl();
		//初始化curl
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		//运行curl，结果以jason形式返回
		$res = curl_exec($ch);
		curl_close($ch);
		//取出openid
		$data = json_decode($res,true);
		return $data;
	}
	
	/**
	 * 作用：通过全局access_token获取客服基本信息
	 */
	function getCustomServiceKFListByOverallAccessToken() {
		$this->getLocalAccessToken();
		$url = $this->createGetKFListUrl();
		//初始化curl
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		//运行curl，结果以jason形式返回
		$res = curl_exec($ch);
		curl_close($ch);
		//取出openid
		$data = json_decode($res,true);
		return $data;
	}
	
	/**
	 * 作用：显示授权获取用户信息
	 */
	function getSnsapiUserinfoByOpenidAndOauthAccessToken($openid, $oauth_access_token) {
		//$this->getUserOpenidAndOauthAccessTokenByCode(); //通过code获取openid和显示授权的access_token
		$this->openid = $openid;
		$this->oauth_access_token = $oauth_access_token;
		$url = $this->createOauthUserInfoUrl();
		//初始化curl
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		//运行curl，结果以jason形式返回
		$res = curl_exec($ch);
		curl_close($ch);
		//取出openid
		$data = json_decode($res,true);
		return $data;
	}
	
	/**
	 * 作用：获取推广链接的ticket
	 * 参数形式:
	 * 		临时二维码：{"expire_seconds": 1800, 
	 * 					"action_name": "QR_SCENE", 
	 * 					"action_info": {"scene": {"scene_id": 123}
	 * 									}
	 *					}
	 * 		永久二维码：{"action_name": "QR_LIMIT_SCENE", 
	 * 					"action_info": {"scene": {"scene_id": 123}
	 * 									}
	 *					}
	 */
	function getQrcodeForTicket($json_post_data) {
		$this->getLocalAccessToken(); // 获取access_token
		$url = $this->createQrcodeTicketUrl();
		//初始化curl
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
		curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		//设置header
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		//要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		//post提交方式
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $json_post_data);
		//运行curl，结果以json形式返回
		$res = curl_exec($ch);
		curl_close($ch);
		$data = json_decode($res,true);
		$this->ticket = $data['ticket'];
		return $this->ticket;
	}
	
	/**
	 * 得到js-sdk-参数
	 * @return multitype:string NULL number
	 */
	public function getSignPackage() {
		$jsapiTicket = $this->getJsApiTicket();
		$url = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
		$timestamp = time();
		$nonceStr = $this->createNonceStr();
		// 这里参数的顺序要按照 key 值 ASCII 码升序排序
		$string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
		$signature = sha1($string);
		$signPackage = array(
				"appId"     => $this->appid,
				"nonceStr"  => $nonceStr,
				"timestamp" => $timestamp,
				"url"       => $url,
				"signature" => $signature,
				"rawString" => $string
		);
		return $signPackage;
	}
	
	/**
	 * 签名
	 * @param number $length
	 * @return string
	 */
	public function createNonceStr($length = 16) {
		$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		$str = "";
		for ($i = 0; $i < $length; $i++) {
			$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
		}
		return $str;
	}
	
	public function getJsApiTicket() {
		// jsapi_ticket 应该全局存储与更新，以下代码以写入到文件中做示例
// 		$url_path = dirname(dirname(__FILE__));
		
		$url_path = C('wesetDir');
		$json_path = $url_path."/lib/config/jsapi_ticket.json";
		$data = json_decode(file_get_contents($json_path));
		if ($data->expire_time < time()) {
			$this->getLocalAccessToken();
			$url = $this->createGetJsTicket();
			$res = json_decode($this->httpGet($url));
			$ticket = $res->ticket;
			if ($ticket) {
				$data->expire_time = time() + 7000;
				$data->jsapi_ticket = $ticket;
				$fp = fopen($json_path, "w");
				fwrite($fp, json_encode($data));
				fclose($fp);
			}
		} else {
			$ticket = $data->jsapi_ticket;
		}
		return $ticket;
	}
	
	/**
	 * 作用：公众号可通过本接口来获取帐号的关注者列表，关注者列表由一串OpenID（加密后的微信号，每个用户对每个公众号的OpenID是唯一的）组成。
	 */
	function getSubscribeList($next_openid) {
		$this->getLocalAccessToken(); // 获取access_token
		$url = $this->createGetSubscribeList($next_openid);
		//初始化curl
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		//运行curl，结果以jason形式返回
		$res = curl_exec($ch);
		curl_close($ch);
		$data = json_decode($res,true);
		return $data;
	}
	
	/**
	 * 作用：获取本地文件中的access_token,判断是否过期，如果过期，则重新生成access_token
	 */
	function getLocalAccessToken() {
		$ACC_TOKEN = "";
		$time = time();
// 		$url_path = dirname(dirname(__FILE__));
		$url_path = C('wesetDir');
		
		
		$xml_path = $url_path."/lib/config/token.xml";
		if(file_exists($xml_path)) {
			//文件存在,解析xml文件取出时间
			$token_arr = (array)simplexml_load_file($xml_path);
			//echo $token_arr['access_token']."--".$token_arr['time'];
			
			if(($time - $token_arr['time']) > C('TOKEN_TIME_OUT_SET')) {
				//超时
				$weixinTokenModel = M('wx_token');
				$tokenRecord = $weixinTokenModel->order('times DESC')->limit(1)->find();
				
				if (!$tokenRecord || ($time - $tokenRecord ['times'] > C('TOKEN_TIME_OUT_SET'))) {
					$ACC_TOKEN = $this->getOverallAccessToken();
					
					$weixinTokenModel->add(array('access_token' => $ACC_TOKEN, 'times' => $time));
					
					$doc = new \DOMDocument("1.0","utf-8");
					$doc -> formatOutput=true; // 设置可输出操作
					$root = $doc->createElement("root"); //设置根节点
					$root = $doc->appendChild($root); //添加根节点
					$name=$doc->createElement("access_token");  //创建父节点name
					$name=$root->appendChild($name);    //把节点添加到menu节点的子节点
					$name->appendChild($doc->createTextNode($ACC_TOKEN));
					
					$type=$doc->createElement("time");  //创建父节点name
					$type=$root->appendChild($type);    //把节点添加到menu节点的子节点
					$type->appendChild($doc->createTextNode($time));
					$doc->save($url_path."/lib/config/token.xml");
				} else if ($tokenRecord) {
					$ACC_TOKEN = $tokenRecord ['access_token'];
				}
			} else {
				//没有超时
				$ACC_TOKEN = $token_arr['access_token'];
				
				//验证没过期的二维码是否有效
				$valid = checkAccessTokenValid($ACC_TOKEN,$token_arr ['time']);
				if(empty($valid)){
					//强制刷新
					//超时
					$ACC_TOKEN = $this->getOverallAccessToken();
					$doc = new DOMDocument("1.0","utf-8");
					$doc -> formatOutput=true; // 设置可输出操作
					$root = $doc->createElement("root"); //设置根节点
					$root = $doc->appendChild($root); //添加根节点
					$name=$doc->createElement("access_token");  //创建父节点name
					$name=$root->appendChild($name);    //把节点添加到menu节点的子节点
					$name->appendChild($doc->createTextNode($ACC_TOKEN));
					
					$type=$doc->createElement("time");  //创建父节点name
					$type=$root->appendChild($type);    //把节点添加到menu节点的子节点
					$type->appendChild($doc->createTextNode($time));
					$doc->save($url_path."/lib/config/token.xml");
				}
				
			}
		} else {
			//取出的appid和appsecret
			$weixinTokenModel = M('wx_token');
			$tokenRecord = $weixinTokenModel->order('times DESC')->limit(1)->find();
				
			if (!$tokenRecord || ($time - $tokenRecord ['times'] > C('TOKEN_TIME_OUT_SET'))) {
				$ACC_TOKEN = $this->getOverallAccessToken();
				$weixinTokenModel->add(array('access_token' => $ACC_TOKEN, 'times' => $time));
				
				$doc = new \DOMDocument("1.0","utf-8");
				$doc -> formatOutput=true; // 设置可输出操作
				$root = $doc->createElement("root"); //设置根节点
				$root = $doc->appendChild($root); //添加根节点
				$name=$doc->createElement("access_token");  //创建父节点name
				$name=$root->appendChild($name);    //把节点添加到menu节点的子节点
				$name->appendChild($doc->createTextNode($ACC_TOKEN));
				
				$type=$doc->createElement("time");  //创建父节点name
				$type=$root->appendChild($type);    //把节点添加到menu节点的子节点
				$type->appendChild($doc->createTextNode($time));
				$doc->save($url_path."/lib/config/token.xml");
			} else if ($tokenRecord) {
				$ACC_TOKEN = $tokenRecord ['access_token'];
			}
		}
		$this->overall_access_token = $ACC_TOKEN;
	}
}